#include "ecfp.h"

#include <assert.h>

#include <memory.h>


void ECFp_Add(const ECFpWork *ec, const ECPoint *a, const ECPoint *b, ECPoint *c) {
	UInteger x, y;
	UInteger t[3]; // 临时变量

	UInteger k; // lambda
 
	// 求斜率
	if (!ECPoint_Equal(a, b)) {
		// k = (y2-y1)/(x2-x1)
		UInteger_ModSub(&a->y, &b->y, &ec->m, &k);
		UInteger_ModSub(&a->x, &b->x, &ec->m, t);
		UInteger_ModDiv(&k, t, &ec->m, &k);
	} else {
		// k = (3*x1 + a) / (2*y1)

		UInteger_FromUInt(&k, 3);
		UInteger_ModMul(&k, &a->x, &ec->m, &k);
		UInteger_ModMul(&k, &a->x, &ec->m, &k);
		UInteger_ModAdd(&k, &ec->a, &ec->m, &k);

		UInteger_FromUInt(t, 2);
		UInteger_ModMul(t, &a->y, &ec->m, t);

		UInteger_ModDiv(&k, t, &ec->m, &k);
	}

	// x = k^2 - x1 - x2
	UInteger_ModMul(&k, &k, &ec->m, &x);
	UInteger_ModSub(&x, &a->x, &ec->m, &x);
	UInteger_ModSub(&x, &b->x, &ec->m, &x);
	
	// y = k(x1-x) - y1
	UInteger_ModSub(&a->x, &x, &ec->m, &y);
	UInteger_ModMul(&k, &y, &ec->m, &y);
	UInteger_ModSub(&y, &a->y, &ec->m, &y);

	UInteger_Copy(&x, &c->x);
	UInteger_Copy(&y, &c->y);
}


bool ECFp_BelongTo(const ECFpWork *ec, const UInteger *x, const UInteger *y) {
	UInteger t[2];

	UInteger_ModMul(x, x, &(ec->m), t);
	UInteger_ModMul(t, t, &(ec->m), t);

	UInteger_ModMul(x, &(ec->a), &(ec->m), t+1);
	UInteger_ModMul(t+1, t, &(ec->m), t);
	UInteger_ModMul(t, &(ec->b), &(ec->m), t);

	UInteger_ModMul(y, y, &(ec->m), t+1);

	return UInteger_Compare(t, t+1) == 0;
}


size_t ECFp_ZA(const ECFpWork *ec, uint8_t *za, size_t zasize) {
	size_t n = (ec->size+7)/8;

	if (za != NULL) {
		uint8_t t[n*2];
		UInteger_ToBytes(&(ec->a), t, n);
		UInteger_ToBytes(&(ec->b), t+n, n);

		memcpy(za, t, zasize>=n*2? n*2 : zasize);

	}

	return n*2;
}

void ECFp_Initialize(ECFpWork *ec, size_t size, const uint8_t *m, const uint8_t *a, const uint8_t *b) {
	assert(ec != NULL && m != NULL);
	
	ec->add = (EC_AddFcn) ECFp_Add;
	ec->mul = (EC_MulFcn) EC_Mul;
	ec->on = (EC_OnFcn) ECFp_BelongTo;
	ec->za = (EC_ZAFcn) ECFp_ZA;
	ec->size = size;

	UInteger_FromBytes(&(ec->m), m, size/8);
	UInteger_FromBytes(&(ec->a), a, size/8);
	UInteger_FromBytes(&(ec->b), b, size/8);
}
